package im.yangqiang.android.unicorn.http;

import android.content.Context;

import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.Volley;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import rx.Observable;
import rx.Subscriber;
import rx.functions.Func1;
import rx.functions.Func2;

/**
 * 网络工具
 * Created by Carlton on 15/9/28.
 */
public class Network
{
    private static final String TAG = Network.class.getSimpleName();
    private static Network                    mInstance;
    private        Context                    mCtx;
    private        RequestQueue               mRequestQueue;
    private        Map<String, List<Request>> mRequests;
    private final String KEY_HASH_CODE = "Key2@ds!v@Carlton!@~as:" + getClass().hashCode();

    private Network()
    {

    }

    private Network(Context context)
    {
        this();
        mCtx = context;
        mRequests = new HashMap<>();
    }

    public static synchronized Network getInstance(Context context)
    {
        if (mInstance == null)
        {
            mInstance = new Network(context);
        }
        return mInstance;
    }

    public RequestQueue getRequestQueue()
    {
        if (mRequestQueue == null)
        {
            mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
        }
        return mRequestQueue;
    }

    public <T> void addToRequestQueue(Request<T> req)
    {
        getRequestQueue().add(req);
    }

    private Observable<String> stringRequest(final String tag, final int type, final String url, final Map<String, String> params)
    {
        return Observable.create(new Observable.OnSubscribe<String>()
        {
            @Override
            public void call(final Subscriber<? super String> subscriber)
            {
                LogStringRequest request = new LogStringRequest(type, url, new Response.Listener<String>()
                {
                    @Override
                    public void onResponse(String response)
                    {
                        if (subscriber.isUnsubscribed())
                        {
                            return;
                        }
                        subscriber.onNext(response);
                        subscriber.onCompleted();
                    }
                }, new Response.ErrorListener()
                {
                    @Override
                    public void onErrorResponse(VolleyError error)
                    {
                        if (subscriber.isUnsubscribed())
                        {
                            return;
                        }
                        subscriber.onError(error);
                    }
                })
                {
                    @Override
                    protected Map<String, String> getParams() throws AuthFailureError
                    {
                        return params;
                    }
                };
                if (mRequests.containsKey(tag))
                {
                    mRequests.get(tag).add(request);
                }
                else
                {
                    ArrayList<Request> value = new ArrayList<>();
                    value.add(request);
                    mRequests.put(tag, value);
                }
                getRequestQueue().add(request);
            }
        });
    }

    /**
     * 取消全部请求
     */
    public void cancelAll()
    {
        for (String key : mRequests.keySet())
        {
            cancel(key);
        }
    }

    /**
     * 取消网络
     *
     * @param tag 网络请求的标识
     */
    public void cancel(String tag)
    {
        if (mRequests.containsKey(tag))
        {
            List<Request> requests = mRequests.get(tag);
            for (Request request : requests)
            {
                request.cancel();
            }
        }
    }

    /**
     * 通过post方式提交数据
     *
     * @param tag    网络请求的标识
     * @param url    请求地址
     * @param params 请求参数
     * @return RxJava Observable
     */
    public Observable<String> post(String tag, final String url, final Map<String, String> params)
    {
        return stringRequest(tag, Request.Method.POST, url, params);
    }

    /**
     * @see #post(String, String, Map)
     */
    public Observable<String> post(final String url, final Map<String, String> params)
    {
        return stringRequest(getClass().getName(), Request.Method.POST, url, params);
    }

    /**
     * 使用post方式请求网络。
     *
     * @param tag    这一条请求的标识
     * @param url    一个可观察者
     * @param params 一个可观察者
     * @return RxJava Observable
     */
    public Observable<String> post(final String tag, final Observable<String> url, Observable<HashMap<String, String>> params)
    {
        return url.zipWith(params, new Func2<String, HashMap<String, String>, HashMap<String, String>>()
        {
            @Override
            public HashMap<String, String> call(final String s, HashMap<String, String> stringStringHashMap)
            {
                stringStringHashMap.put(KEY_HASH_CODE, s);
                return stringStringHashMap;
            }
        }).flatMap(new Func1<HashMap<String, String>, Observable<? extends String>>()
        {
            @Override
            public Observable<? extends String> call(HashMap<String, String> map)
            {
                String url = map.get(KEY_HASH_CODE);
                map.remove(KEY_HASH_CODE);
                return post(tag, url, map);
            }
        });
    }

    /**
     * @see #post(String, Observable, Observable)
     */
    public Observable<String> post(Observable<String> url, Observable<HashMap<String, String>> params)
    {
        return post(getClass().getName(), url, params);
    }

    /**
     * 通过get方式
     *
     * @param tag    网络请求的标识
     * @param url    请求地址
     * @param params 请求参数
     * @return RxJava Observable
     */
    public Observable<String> get(String tag, String url, Map<String, String> params)
    {
        return stringRequest(tag, Request.Method.GET, url, params);
    }

    /**
     * @see #get(String, String, Map)
     */
    public Observable<String> get(String url, Map<String, String> params)
    {
        return stringRequest(getClass().getName(), Request.Method.GET, url, params);
    }

    /**
     * 使用get方式请求网络。
     *
     * @param tag    这一条请求的标识
     * @param url    一个可观察者
     * @param params 一个可观察者
     * @return RxJava Observable
     */
    public Observable<String> get(final String tag, final Observable<String> url, Observable<HashMap<String, String>> params)
    {
        return url.zipWith(params, new Func2<String, HashMap<String, String>, HashMap<String, String>>()
        {
            @Override
            public HashMap<String, String> call(final String s, HashMap<String, String> stringStringHashMap)
            {
                stringStringHashMap.put(KEY_HASH_CODE, s);
                return stringStringHashMap;
            }
        }).flatMap(new Func1<HashMap<String, String>, Observable<? extends String>>()
        {
            @Override
            public Observable<? extends String> call(HashMap<String, String> map)
            {
                String url = map.get(KEY_HASH_CODE);
                map.remove(KEY_HASH_CODE);
                return get(tag, url, map);
            }
        });
    }

    /**
     * @see #get(String, Observable, Observable)
     */
    public Observable<String> get(Observable<String> url, Observable<HashMap<String, String>> params)
    {
        return get(getClass().getName(), url, params);
    }
}



